/* Copyright (c) 2010, Carl Burch. License information is located in the
 * com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */

package com.cburch.draw.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;

import com.cburch.draw.model.CanvasModel;
import com.cburch.draw.model.CanvasObject;

public class ZOrder {
    private ZOrder() {
    	
    }

    public static int getZIndex(CanvasObject query, CanvasModel model) {
        // returns 0 for bottommost element, large number for topmost
        return getIndex(query, model.getObjectsFromBottom());
    }

    public static Map<CanvasObject, Integer> getZIndex(
            Collection<? extends CanvasObject> query, CanvasModel model) {
        // returns 0 for bottommost element, large number for topmost, ordered
        // from the bottom up.
        if (query == null) {
            return Collections.emptyMap();
        }


        Set<? extends CanvasObject> querySet = toSet(query);
        Map<CanvasObject, Integer> ret;
        ret = new LinkedHashMap<CanvasObject, Integer>(query.size());
        int z = -1;
        for (CanvasObject o : model.getObjectsFromBottom()) {
            z++;
            if (querySet.contains(o)) {
                ret.put(o, Integer.valueOf(z));
            }
        }
        return ret;
    }

    public static <E extends CanvasObject> List<E> sortTopFirst(
            Collection<E> objects, CanvasModel model) {
        return sortXFirst(objects, model, model.getObjectsFromBottom());
    }

    public static <E extends CanvasObject> List<E> sortBottomFirst(
            Collection<E> objects, CanvasModel model) {
        return sortXFirst(objects, model, model.getObjectsFromTop());
    }

    private static <E extends CanvasObject> List<E> sortXFirst(
            Collection<E> objects, CanvasModel model, Collection<CanvasObject> objs) {
        Set<E> set = toSet(objects);
        List<E> ret = new ArrayList<E>(objects.size());
        for (CanvasObject o : objs) {
            if (set.contains(o)) {
                @SuppressWarnings("unchecked")
                E toAdd = (E) o;
                ret.add(toAdd);
            }
        }
        return ret;
    }

    private static <E> Set<E> toSet(Collection<E> objects) {
        if (objects instanceof Set) {
            return (Set<E>) objects;
        } else {
            return new HashSet<E>(objects);
        }
    }

    // returns first object above query in the z-order that overlaps query
    public static CanvasObject getObjectAbove(CanvasObject query,
            CanvasModel model, Collection<? extends CanvasObject> ignore) {
        return getPrevious(query, model.getObjectsFromTop(), model, ignore);
    }

    // returns first object below query in the z-order that overlaps query
    public static CanvasObject getObjectBelow(CanvasObject query,
            CanvasModel model, Collection<? extends CanvasObject> ignore) {
        return getPrevious(query, model.getObjectsFromBottom(), model, ignore);
    }

    private static CanvasObject getPrevious(CanvasObject query,
            List<CanvasObject> objs, CanvasModel model,
            Collection<? extends CanvasObject> ignore) {
        int index = getIndex(query, objs);
        if (index <= 0) {
            return null;
        } else {
            Set<CanvasObject> set = toSet(model.getObjectsOverlapping(query));
            ListIterator<CanvasObject> it = objs.listIterator(index);
            while (it.hasPrevious()) {
                CanvasObject o = it.previous();
                if (set.contains(o) && !ignore.contains(o)) {
                    return o;
                }

            }
            return null;
        }
    }


    private static int getIndex(CanvasObject query, List<CanvasObject> objs) {
        int index = -1;
        for (CanvasObject o : objs) {
            index++;
            if (o == query) {
                return index;
            }

        }
        return -1;
    }

}
